package org.pylates;

import java.io.Reader;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.python.core.Py;
import org.python.core.PyCode;
import org.python.core.PyObject;
import org.python.util.PythonInterpreter;

public class PythonFacade {

	private final static PythonInterpreter pyInterp = new PythonInterpreter();

	public PythonFacade() {
	}

	/**
	 * Build from Reader
	 * 
	 * @return
	 */
	public static PythonFacade BuildFrom(Reader reader) {
		PythonFacade result = new PythonFacade();
		result.compile(reader);
		return result;
	}

	/**
	 * Get python interpreter
	 * 
	 * @return PythonInterpreter
	 */
	public static PythonInterpreter getPythonInterpreter() {
		return pyInterp;
	}

	/**
	 * Compile code
	 * 
	 * @param reader
	 *            Reader
	 */
	public void compile(Reader reader) {
		PyCode code = pyInterp.compile(reader);
		pyInterp.exec(code);
	}

	/**
	 * Build and return new template
	 * 
	 * @param name
	 *            String
	 * @return PyTemplate
	 */
	public PyTemplate buildPyTemplate(String name) {
		PyObject pyObject = pyInterp.get(name);
		if (pyObject.__findattr__("__call__") == null)
			// is not a callable object
			return null;
		return new PyTemplate(pyObject);
	}

	/**
	 * Invoke function object associated with template
	 * 
	 * @param pyTemplate PyTemplate
	 * @return String
	 * @throws PyTemplateException
	 */
	public String invoke(PyTemplate pyTemplate) throws PyTemplateException {
		PyObject pyFunction = pyTemplate.getAttachedFunction();
		if (pyFunction == null) {
			throw new PyTemplateException("Function can't be null");
		}
		PyObject output = null;
		// check formal arguments
		if (pyTemplate.getAttributes() != null && pyTemplate.getAttributes().size() > 0) {
			// prepare formal parameters
			int count = pyTemplate.getAttributes().size();
			PyObject[] pyArgs = new PyObject[count];
			String[] pyKeywords = new String[count];
			Iterator iterator = pyTemplate.getAttributes().entrySet().iterator();
			for (int i = 0; iterator.hasNext(); i++) {
				Map.Entry entry = (Entry) iterator.next();
				pyKeywords[i] = (String) entry.getKey();
				pyArgs[i] = Py.newString((String) entry.getValue());
			}
			// invoke
			output = pyFunction.__call__(pyArgs, pyKeywords);
		} else {
			// prepare no formal parameters
			output = pyFunction.__call__();
		}
		if (output != null) {
			return output.toString();
		}
		return null;
	}
}
